Storing data locally is a common task for mobile applications. Such data includes preferences or authentication tokens for external services, among
other things. There are many convenient solutions that allow storing data persistently, for example SQLiteDatabase, SharedPreferences, and Realm. By
default these systems store the data unencrypted, thus an attacker with physical access to the device can read them out easily. Access to sensitive
data can be harmful for the user of the application, for example when the device gets stolen.
Ask Yourself Whether
- The database contains sensitive data that could cause harm when leaked.
There is a risk if you answered yes to any of those questions.
Recommended Secure Coding Practices
It’s recommended to password-encrypt local databases that contain sensitive information. Most systems provide secure alternatives to plain-text
storage that should be used. If no secure alternative is available the data can also be encrypted manually before it is stored.
The encryption password should not be hard-coded in the application. There are different approaches how the password can be provided to encrypt and
decrypt the database. In the case of EncryptedSharedPreferences
the Android Keystore can be used to store the password. Other databases
can rely on EncryptedSharedPreferences
to store passwords. The password can also be provided dynamically by the user of the application
or it can be fetched from a remote server if the other methods are not feasible.
Sensitive Code Example
For SQLiteDatabase:
var db = activity.openOrCreateDatabase("test.db", Context.MODE_PRIVATE, null) // Sensitive
For SharedPreferences:
val pref = activity.getPreferences(Context.MODE_PRIVATE) // Sensitive
For Realm:
val config = RealmConfiguration.Builder().build()
val realm = Realm.getInstance(config) // Sensitive
Compliant Solution
Instead of SQLiteDatabase you can use SQLCipher:
val db = SQLiteDatabase.openOrCreateDatabase("test.db", getKey(), null)
Instead of SharedPreferences you can use EncryptedSharedPreferences:
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
EncryptedSharedPreferences.create(
"secret",
masterKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
For Realm an encryption key can be specified in the config:
val config = RealmConfiguration.Builder()
.encryptionKey(getKey())
.build()
val realm = Realm.getInstance(config)
See